package com.terrace.core.spring.ehcache.interceptor;

import java.io.IOException;
import java.lang.reflect.Method;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.terrace.core.spring.ehcache.annotation.EhCacheEvict;
import com.terrace.core.spring.ehcache.annotation.EhCacheable;
import com.terrace.core.spring.support.BaseAspectSupport;
import com.terrace.core.utils.EHCacheUtils;
import com.terrace.core.utils.JsonUtils;

/**
 * 缓存切面支撑类
 * @author jiangyg
 *
 */
public abstract class EhCacheAspectSupport extends BaseAspectSupport {
	
	/**
	 * 日志
	 */
	protected static final Logger logger = LoggerFactory.getLogger(EhCacheAspectSupport.class);

	/**
	 * 执行
	 * @param cacheInvoker
	 * @param target
	 * @param method
	 * @param args
	 * @return
	 */
	protected Object execute(EhCacheInvoker cacheInvoker, Object target, Method method, Object[] args) {
		
		// 获取注解所在类
		Class<?> targetClass = getTargetClass(target);
		
		// 获取缓存
		EhCacheable cacheable = method.getAnnotation(EhCacheable.class);
		if (cacheable != null) {
			return executeEhCacheable(cacheable, cacheInvoker, targetClass, method, args);
		}
		
		// 移除缓存
		EhCacheEvict cacheEvict = method.getAnnotation(EhCacheEvict.class);
		if (cacheEvict != null) {
			return executeEhCacheEvict(cacheEvict, cacheInvoker, method);
		}
		
		return cacheInvoker.invoke();
	}
	
	/**
	 * 执行获取缓存方法
	 * @param cacheInvoker
	 * @param targetClass
	 * @param method
	 * @param args
	 * @return
	 */
	private Object executeEhCacheable(EhCacheable cacheable, EhCacheInvoker cacheInvoker, Class<?> targetClass, Method method, Object[] args) {
		
		if (StringUtils.isBlank(cacheable.name())) {
			logger.error("缓存出错，未获取到详细的注解信息。");
			return cacheInvoker.invoke();
		}
		
		try {
			
			// 缓存key没设置，则默认用方法+参数
			String mainCacheKey = StringUtils.isBlank(cacheable.key()) ? (targetClass.getName() + "." + method.getName()) : cacheable.key();
			// 缓存Key
			String cacheKey = getCacheKey(mainCacheKey, args);
			// 获取缓存中的值
			Object result = EHCacheUtils.get(cacheable.name(), cacheKey);
			
			if (logger.isDebugEnabled()) {
				logger.debug("缓存[" + cacheKey + "]获取到的值为：" + JsonUtils.beanToJson(result));
			}
			
			if (result == null) {
				result = cacheInvoker.invoke();
				EHCacheUtils.put(cacheable.name(), cacheKey, result);
			}
			return result;
			
		} catch (IOException e) {
			logger.error("缓存AOP出错：", e);
		}
		
		return cacheInvoker.invoke();
	}
	
	/**
	 * 清除缓存操作
	 * @param cacheEvict
	 * @param method
	 */
	private Object executeEhCacheEvict(EhCacheEvict cacheEvict, EhCacheInvoker cacheInvoker, Method method) {
		
		if (StringUtils.isBlank(cacheEvict.name())) {
			logger.error("缓存清除出错，未获取到详细的注解信息。");
			return cacheInvoker.invoke();
		}

		Object result = null;
		
		if (cacheEvict.beforeInvocation()) {
			clearCache(cacheEvict);
		}
		
		result = cacheInvoker.invoke();
		
		if (!cacheEvict.beforeInvocation()) {
			clearCache(cacheEvict);
		}

		return result;
	}
	
	/**
	 * 清除缓存
	 * @param cacheEvict
	 */
	private void clearCache(EhCacheEvict cacheEvict) {
		
		// debug
		if (logger.isDebugEnabled()) {
			if (!cacheEvict.all()) {
				logger.debug("清除[" + cacheEvict.name() + "]-[" + JsonUtils.beanToJson(cacheEvict.key()) + "*]的缓存");
			} else {
				logger.debug("清除[" + cacheEvict.name() + "]的缓存");
			}
		}

		// 清除缓存
		try {
			if (cacheEvict.all()) {
				EHCacheUtils.empty(cacheEvict.name());
			} else {
				String[] cacheKeys = cacheEvict.key();
				for (String cacheKey : cacheKeys) {
					EHCacheUtils.fuzzyRemove(cacheEvict.name(), cacheKey);
				}
			}
		} catch (IOException e) {
			logger.error("清除缓存报错：", e);
		}
		
	}
	
	/**
	 * 类名/cahceKey+参数
	 * 
	 * @param mainCahceKey
	 * @param arguments
	 * @return
	 */
	private String getCacheKey(String mainCahceKey, Object[] arguments) {
		StringBuffer sb = new StringBuffer();
		sb.append(mainCahceKey);
		if ((arguments != null) && (arguments.length != 0)) {
			for (int i = 0; i < arguments.length; i++) {
				sb.append(".").append(JsonUtils.beanToJson(arguments[i]));
			}
		}
		return sb.toString();
	}

}
